package apifm

import (
	"context"
	"fmt"
	"strings"
	"time"

	"mall/common/globalkey"
	"mall/common/jwt"
	"mall/common/resultx"
	"mall/common/utils/pointy"
	"mall/service/forum/api/internal/svc"
	"mall/service/forum/api/internal/types"
	"mall/service/user/rpc/userclient"

	wechat "github.com/silenceper/wechat/v2"
	"github.com/silenceper/wechat/v2/cache"
	miniConfig "github.com/silenceper/wechat/v2/miniprogram/config"

	// "github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type LoginLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
	return &LoginLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *LoginLogic) Login(
	req *types.MockLoginReq,
	headers *[]*types.Header,
	ip string,
) (*types.MockLoginResp, error) {
	var loginMethod string
	if req.LoginMethod == nil || len(strings.TrimSpace(*req.LoginMethod)) == 0 {
		loginMethod = globalkey.UserAuthTypeWechat
	} else {
		loginMethod = *req.LoginMethod
	}

	var data *types.MockLoginInfo
	var err error
	if loginMethod == globalkey.UserAuthTypeWechat {
		if req.Code == nil || len(strings.TrimSpace(*req.Code)) == 0 {
			return nil, resultx.NewErrCode(resultx.REUQEST_PARAM_ERROR)
		}

		data, err = l.wxMiniLogin(strings.TrimSpace(*req.Code), req.Force, ip)
		if err != nil {
			return nil, err
		}
	}

	if data != nil {
		now := time.Now().Unix()
		accessExpire := l.svcCtx.Config.Auth.AccessExpire
		var securityStatus string
		switch int32(data.Status) {
		case globalkey.StatusDisabled:
			securityStatus = jwt.SUSPENDED
		case globalkey.StatusExpired:
			securityStatus = jwt.CANCELLED
		case globalkey.StatusDeleted:
			securityStatus = jwt.BANNED
		case globalkey.StatusAsGuest:
			securityStatus = jwt.ANONYMOUS
		default:
			securityStatus = jwt.CERTIFICATED
		}

		token, err := jwt.NewJwtToken(
			l.svcCtx.Config.Auth.AccessSecret,
			now,
			accessExpire,
			jwt.WithOption("userId", data.UserId),
			jwt.WithOption("rankId", data.RankId),
			jwt.WithOption("role", data.Role),
			jwt.WithOption("securityStatus", securityStatus),
		)
		if err != nil {
			logx.Errorw("Login NewJwtToken err", logx.Field("detail", err.Error()))
			return nil, resultx.NewErrCode(resultx.USER_LOGIN_WXMINI_FAILED)
		}

		// *headers = []*types.Header{{
		// 	Key:   "x-class",
		// 	Value: "com.djx.DatabaseException",
		// }}
		// *headers = []*types.Header{{
		// 	Key: "x-navigation-method",
		// }}
		// *headers = []*types.Header{{
		// 	Key: "x-env-type",
		// }}
		*headers = []*types.Header{{
			Key:   "authorization",
			Value: token,
		}}

		return &types.MockLoginResp{
			Success: true,
		}, nil
	}

	return &types.MockLoginResp{}, nil
}

func (l *LoginLogic) wxMiniLogin(
	code string,
	force *bool,
	ip string,
) (resp *types.MockLoginInfo, err error) {
	miniprogram := wechat.NewWechat().GetMiniProgram(&miniConfig.Config{
		AppID:     l.svcCtx.Config.WxMiniConf.AppId,
		AppSecret: l.svcCtx.Config.WxMiniConf.Secret,
		Cache:     cache.NewMemory(),
	})
	authRes, err := miniprogram.GetAuth().Code2Session(code)
	if err != nil {
		logx.Errorw("Login wxMiniLogin Code2Session err", logx.Field("detail", err.Error()))
		// errcode=40029 , errmsg=invalid code, rid: 65d9d79d-2a5c278d-3e230b3d
		// errcode=40163 , errmsg=code been used, rid: 6396b5fe-76b76b6d-00c4a74f
		return nil, resultx.NewErrCode(resultx.USER_LOGIN_WXMINI_FAILED)
	}

	if authRes.ErrCode != 0 || len(authRes.OpenID) == 0 {
		logx.Errorw("Login wxMiniLogin Code2Session failed", logx.Field("detail", fmt.Sprintf("%+v ", authRes)))
		// CommonError:{apiName: ErrCode:0 ErrMsg:}
		// CommonError:{apiName: ErrCode:40163 ErrMsg:code been used, rid: 6396b5fe-76b76b6d-00c4a74f}
		return nil, resultx.NewErrCode(resultx.USER_LOGIN_WXMINI_FAILED)
	}

	// logrus.Info(fmt.Sprintf("Login wxMiniLogin Code2Session res : %+v ", authRes))
	// OpenID:o-kUG5pMqzsclBbFlUdZNJ2OW4_o
	// SessionKey:FAXeGbFV8dDkEeTtLWaaHw==
	// UnionID:

	var userId string
	var status uint32
	var rankId uint64
	var role uint32
	var platformId uint32 = globalkey.ThirdPartyPlatformWechatMini
	var notAsGuest *uint32
	if force != nil && *force {
		notAsGuest = pointy.GetPointer(uint32(1))
	}
	res, err := l.svcCtx.UserRpc.GetUserInfoByAuthKey(l.ctx, &userclient.GetUserInfoByAuthKeyReq{
		PlatformId: pointy.GetPointer(platformId),
		AuthKey:    pointy.GetPointer(authRes.OpenID),
		Force:      notAsGuest,
	})
	if err != nil {
		logx.Errorw("Login wxMiniLogin GetUserInfoByAuthKey err", logx.Field("detail", err.Error()))
		return nil, resultx.NewErrCode(resultx.USER_LOGIN_WXMINI_FAILED)
	}

	if res.User == nil || res.User.Id == nil {
		registerRes, err := l.svcCtx.UserRpc.CreateUserAuthInfo(l.ctx, &userclient.TmpUserAuthInfo{
			PlatformId: pointy.GetPointer(platformId),
			Openid:     pointy.GetPointer(authRes.OpenID),
			Unionid:    pointy.GetPointer(authRes.UnionID),
			SessionKey: pointy.GetPointer(authRes.SessionKey),
			CreatedIP:  pointy.GetPointer(ip),
		})

		if err != nil {
			logx.Errorw("Login wxMiniLogin CreateUserAuthInfo err", logx.Field("detail", err.Error()))
			return nil, resultx.NewErrCode(resultx.USER_LOGIN_WXMINI_FAILED)
		}

		userId = *registerRes.Id
		status = *registerRes.Status
		rankId = *registerRes.RankId
		role = *registerRes.Role
	} else if len(*res.User.Id) > 0 {
		userId = *res.User.Id
		status = *res.User.Status
		if res.UserDetail != nil || res.UserDetail.Role != nil {
			role = *res.UserDetail.Role
		}
		if res.UserAccount != nil || res.UserAccount.RankId != nil {
			rankId = *res.UserAccount.RankId
		}
	}

	return &types.MockLoginInfo{
		UserId: userId,
		Status: status,
		RankId: rankId,
		Role:   role,
	}, nil
}
